/**
*FileContentManager is a Java based file manager desktop application, 
*it can show, edit and manipulate the content of the files archived inside a zip.
*
*Copyright (C) 2007 
*
*Created by Camila Sanchez [http://mimix.wordpress.com/], Rafael Naufal [http://rnaufal.livejournal.com] and Rodrigo [rdomartins@gmail.com]
*
*FileContentManager is free software; you can redistribute it and/or
*modify it under the terms of the GNU Lesser General Public
*License as published by the Free Software Foundation; either
*version 2.1 of the License, or (at your option) any later version.
*
*This library is distributed in the hope that it will be useful,
*but WITHOUT ANY WARRANTY; without even the implied warranty of
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
*Lesser General Public License for more details.
*
*You should have received a copy of the GNU Lesser General Public
*License along with FileContentManager; if not, write to the Free Software
*Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package br.com.jteam.jfcm.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import br.com.jteam.jfcm.controller.event.Event;

/**
 * Classe controladora da notifica&ccedil;&atilde;o de eventos. Registra
 * subscribers de eventos e notifica todos eles da ocorr&ecirc;ncia de um
 * determinado evento. O objetivo dessa classe &eacute; entregar um evento para
 * o componente que deve manipul&aacute;-lo de forma a separar o emissor do
 * receptor de eventos. Um caso t&iacute;pico de uso seria quando uma
 * a&ccedil;&atilde;o exercida pela usu&aacute;rio sobre um objeto causa
 * altera&ccedil;&otilde;es em outro, que originalmente n&atilde;o responde - ao
 * menos n&atilde;o explicitamente - a a&ccedil;&otilde;es do usu&aacute;rio. O
 * objeto que sofre a a&ccedil;&atilde;o lan&ccedil;a um {@link Event}
 * espec&iacute;fico, que ser&aacute; manipulado por outro objeto.
 * 
 * @see Subscriber
 * @see Event
 */
public final class EventDispatchService
{
	/**
	 * Inst&acirc;ncia do servi&ccedil;o de despacho de eventos. Deve haver
	 * apenas uma inst&acirc;ncia dessa classe durante toda a vida do sistema.
	 */
	private static final EventDispatchService SERVICE_INSTANCE;

	static
	{
		SERVICE_INSTANCE = new EventDispatchService();
	}

	/**
	 * Retorna a inst&acirc;ncia do servi&ccedil;o de notifica&ccedil;&atilde;o
	 * de eventos.
	 * 
	 * @return A inst&acirc;ncia do servi&ccedil;o de notifica&ccedil;&atilde;o
	 *         de eventos.
	 */
	public static EventDispatchService getInstance()
	{
		return SERVICE_INSTANCE;
	}

	/**
	 * Mapeia uma classe de eventos para uma cole&ccedil;&atilde;o de
	 * subscribers que ser&atilde;o notificados da sua ocorr&ecirc;ncia.
	 */
	private Map<Class<? extends Event>, List<Subscriber>> dispathMapper;

	{
		dispathMapper = new HashMap<Class<? extends Event>, List<Subscriber>>();
	}

	/**
	 * Construtor padr&atilde;o. Inst&acirc;ncias dessa classe devem ser
	 * constru&iacute;das a partir do factory method {@link #getInstance()}
	 */
	private EventDispatchService()
	{
		// essa classe det&eacute;m o controle do processo de
		// cria&ccedil;&atilde;o de inst&acirc;ncias.
	}

	/**
	 * Notifica todos os subscribers registrados da ocorr&ecirc;ncia de um
	 * evento.
	 * 
	 * @param event
	 *            O evento da notifica&ccedil;&atilde;o. N&atilde;o pode ser
	 *            nulo.
	 * @throws IllegalArgumentException
	 *             Caso o par&acirc;metro seja nulo.
	 */
	public void fireEvent(Event event)
	{
		if (event == null)
		{
			throw new IllegalArgumentException("Event cannot be null.");
		}
		List<Subscriber> subscribers = dispathMapper.get(event.getClass());
		if (subscribers != null)
		{
			List<Subscriber> list = new ArrayList<Subscriber>(subscribers);
			for (Subscriber sub : list)
			{
				sub.notify(event);
			}
		}
	}

	/**
	 * Cadastra um Subscriber a ser notificado para ocorr&ecirc;ncias de uma
	 * tipo de evento.
	 * 
	 * @param eventClass
	 *            Classe do evento.
	 * @param subscriber
	 *            Objeto a ser notificado.
	 * @throws IllegalArgumentException
	 *             Caso um dos par&acirc;metros seja nulo.
	 */
	public void subscribe(	Class<? extends Event> eventClass,
							Subscriber subscriber)
	{
		List<Subscriber> subscribers;
		if (eventClass == null)
		{
			throw new IllegalArgumentException("Event class cannot be null.");
		}
		if (subscriber == null)
		{
			throw new IllegalArgumentException("Subscriber cannot be null.");
		}
		if (!dispathMapper.containsKey(eventClass))
		{
			subscribers = new LinkedList<Subscriber>();
			dispathMapper.put(eventClass, subscribers);
		} else
		{
			subscribers = dispathMapper.get(eventClass);
		}
		subscribers.add(subscriber);
	}

}
